"""Main entry point for site control tool."""

import getpass
import optparse
import sys
import textwrap

usage = """hcp-site [options] <command> <site_directory>

This command is used to install, upgrade, and manage a Hostmate Control Panel
installation.  The first argument is the command you want to run.  The second
argument is the directory where you want to install the Hostmate Control Panel
files.

"""

class Site(object):

    """Core site class for all shared code.

    :IVariables:
        - `options`: Command-line options.
        - `site_dir`: Site directory.
    """

    def run(self):
        """Main entry point."""
        import hcp.site.install
        COMMANDS = [
            hcp.site.install.Command(self),
        ]

        parser = optparse.OptionParser(usage=usage)

        for command in COMMANDS:
            command.add_options(parser)

        self.options, args = parser.parse_args(sys.argv[1:])

        if len(args) != 2:
            parser.error('You must specify two arguments, the command to run and the site directory.')

        command_name = args[0]
        self.site_dir = args[1]

        for command in COMMANDS:
            if command.name == command_name:
                command.run()
                break
        else:
            parser.error('Unknown command: %r' % (command_name,))

    def exit(self, code):
        """Exit the process."""
        sys.exit(code)

    def _print(self, message, with_newline=True, word_wrap=True):
        """Print text to the terminal.

        :Parameters:
            - `message`: The message to display.
            - `with_newline`: Whether or not to add a newline after the
              message.
            - `word_wrap`: Whether or not to word wrap the message.
        """
        if word_wrap:
            # Textwrap seems to convert empty starting lines to spaces, which
            # is not really what I want.
            while message.startswith('\n'):
                sys.stdout.write('\n')
                message = message[1:]
            # Textwrap assumes a single paragraph, not really what I want.
            paragraphs = message.split('\n\n')
            wrapped = []
            for paragraph in paragraphs:
                wrapped.append(textwrap.fill(paragraph))
            sys.stdout.write('\n'.join(wrapped))
        else:
            sys.stdout.write(message)
        if with_newline:
            sys.stdout.write('\n')
        sys.stdout.flush()

    def prompt(self, message=None, default=None, validator=None,
                     validator_message=None,
                     hide_input=False, input_required=True,
                     newline=True):
        """Prompt for input from the user.

        :Parameters:
            - `message`: The message to print before the prompt.
            - `default`: The default value.
            - `validator`: A function to call to validate the input.
              Takes one parameter with the user's input.  Should
              return the resulting value, or raise ValueError if it
              is not valid.
            - `validator_message`: If the validator fails, this is the message
              displayed. Should explain what the valid input is.
            - `hide_input`: Whether or not to hide input (for passwords, for
              example).
            - `input_required`: Whether or not input is required.  If not
              required, and no default is given, then the empty string is
              returned.
            - `newline`: Whether or not to print an extra newline after the
              input (simplifies asking multiple questions in a row).
        """
        while 1:
            if message:
                self._print(message)

            if default:
                prompt = '[%s]> ' % (default,)
            else:
                prompt = '> '

            self._print(prompt, with_newline=False, word_wrap=False)

            if hide_input:
                response = getpass.getpass('')
            else:
                response = raw_input()

            if not response:
                if default:
                    response = default
                elif input_required:
                    self._print('A response is required.')
                    continue
                else:
                    break

            response = response.strip()
            if validator is not None:
                try:
                    response = validator(response)
                except ValueError:
                    self._print('%r is not a valid response.' % (response,))
                    if validator_message:
                        self._print(validator_message)
                    continue
            break

        if newline:
            self._print('')
        return response

    def query(self, question, default):
        """Ask a yes/no question.

        :Parameters:
            - `question`: The question text.
            - `default`: The default (True=Yes, False=No).

        :Return:
            Returns True or False.
        """
        if default:
            default_input = 'Y'
        else:
            default_input = 'N'

        def validator(response):
            if response.lower() in ('y', 'yes'):
                return True
            elif response.lower() in ('n', 'no'):
                return False
            else:
                raise ValueError

        return self.prompt(question, default_input,
                           validator=validator,
                           validator_message='Please enter "Yes" or "No".')

    def run_manage_command(self, cmd, params=None):
        """Run a django management command.

        :Parameters:
            - `cmd`: The command to run.
            - `params`: List of parameter strings.
        """
        from django.core.management import execute_manager
        import hcp.settings

        if params is None:
            params = []

        execute_manager(hcp.settings, [sys.argv[0], cmd] + params)

if __name__ == '__main__':
    s = Site()
    s.run()
